// 封装 Fetch 请求，Chrome49+ 通过降级到 XMLHttpRequest 兼容

const DEFAULT_BASE_URL = '/';
const DEFAULT_TIMEOUT = 8000; // ms

function isPlainObject(value) {
  return Object.prototype.toString.call(value) === '[object Object]';
}

function isFormData(value) {
  return typeof FormData !== 'undefined' && value instanceof FormData;
}

function buildQueryString(params) {
  if (!params) return '';
  const parts = [];
  Object.keys(params).forEach((key) => {
    const val = params[key];
    if (val === undefined || val === null) return;
    if (Array.isArray(val)) {
      val.forEach((v) => parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`));
    } else if (isPlainObject(val)) {
      parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(val))}`);
    } else {
      parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(val)}`);
    }
  });
  return parts.length ? `?${parts.join('&')}` : '';
}

function joinURL(baseURL, url) {
  if (!baseURL) return url;
  if (/^https?:\/\//i.test(url)) return url;
  const slash = baseURL.endsWith('/') || url.startsWith('/') ? '' : '/';
  const trimmedBase = baseURL.endsWith('/') && url.startsWith('/') ? baseURL.slice(0, -1) : baseURL;
  return `${trimmedBase}${slash}${url}`;
}

function shouldHandleAsJSON(body, headers) {
  if (isFormData(body)) return false;
  const contentType = headers && (headers['Content-Type'] || headers['content-type']);
  if (!contentType) return true; // 默认 JSON
  return /application\/json/i.test(contentType);
}

function parseResponseJSONSafely(text) {
  try {
    return JSON.parse(text);
  } catch (e) {
    return text;
  }
}

function normalizeHeaders(headers) {
  const result = {};
  if (!headers) return result;
  Object.keys(headers).forEach((k) => {
    result[k] = headers[k];
  });
  return result;
}

function handleBusinessResponse(res) {
  // 与 axios 版保持一致：若存在 code 且为 -1，则 reject；否则返回 res.data
  if (res && typeof res === 'object' && 'code' in res) {
    if (res.code === -1) {
      const msg = res.message || '未知错误，请联系管理员查看';
      console.error('[api]', msg);
      return Promise.reject(msg);
    }
    return res.data;
  }
  return res;
}

function requestWithFetch(finalUrl, { method, headers, body, timeout }) {
  const normalizedHeaders = normalizeHeaders(headers);
  const useJson = shouldHandleAsJSON(body, normalizedHeaders);
  const hasBody = body !== undefined && body !== null && method !== 'GET' && method !== 'HEAD';

  const init = {
    method,
    headers: Object.assign(
      { Accept: 'application/json, text/plain, */*' },
      useJson && hasBody && !normalizedHeaders['Content-Type']
        ? { 'Content-Type': 'application/json;charset=UTF-8' }
        : {},
      normalizedHeaders
    ),
    credentials: 'same-origin',
  };

  if (hasBody) {
    init.body = useJson && !isFormData(body) ? JSON.stringify(body) : body;
  }

  const supportsAbort = typeof AbortController !== 'undefined';
  let controller;
  let timerId;
  if (supportsAbort) {
    controller = new AbortController();
    init.signal = controller.signal;
    if (timeout && timeout > 0) {
      timerId = setTimeout(() => controller.abort(), timeout);
    }
  }

  return fetch(finalUrl, init).then(async (resp) => {
    if (timerId) clearTimeout(timerId);
    const contentType = resp.headers && resp.headers.get ? resp.headers.get('content-type') : '';

    let data;
    if (/application\/json/i.test(contentType || '')) {
      data = await resp.json().catch(() => null);
    } else {
      const text = await resp.text();
      data = parseResponseJSONSafely(text);
    }

    if (!resp.ok) {
      const message = (data && (data.message || data.msg)) || `HTTP ${resp.status}`;
      console.error('[api]', message);
      return Promise.reject(message);
    }

    return handleBusinessResponse(data);
  }).catch((err) => {
    if (err && err.name === 'AbortError') {
      return Promise.reject('请求超时');
    }
    const message = err && err.message ? err.message : err;
    console.error('[api]', message);
    return Promise.reject(message);
  });
}

function requestWithXHR(finalUrl, { method, headers, body, timeout }) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open(method, finalUrl, true);
    if (timeout && timeout > 0) xhr.timeout = timeout;

    const normalizedHeaders = normalizeHeaders(headers);
    const useJson = shouldHandleAsJSON(body, normalizedHeaders);
    const hasBody = body !== undefined && body !== null && method !== 'GET' && method !== 'HEAD';

    // 默认头
    xhr.setRequestHeader('Accept', 'application/json, text/plain, */*');
    if (useJson && hasBody && !normalizedHeaders['Content-Type'] && !isFormData(body)) {
      xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
    }
    Object.keys(normalizedHeaders).forEach((k) => {
      xhr.setRequestHeader(k, normalizedHeaders[k]);
    });

    xhr.onreadystatechange = function onReadyStateChange() {
      if (xhr.readyState !== 4) return;
      if (xhr.status >= 200 && xhr.status < 300) {
        const contentType = xhr.getResponseHeader && xhr.getResponseHeader('content-type');
        let data;
        if (/application\/json/i.test(contentType || '')) {
          data = parseResponseJSONSafely(xhr.responseText);
        } else {
          data = parseResponseJSONSafely(xhr.responseText);
        }
        resolve(handleBusinessResponse(data));
      } else {
        const message = `HTTP ${xhr.status || 0}`;
        console.error('[api]', message);
        reject(message);
      }
    };

    xhr.ontimeout = function onTimeout() {
      reject('请求超时');
    };

    xhr.onerror = function onError() {
      reject('网络错误');
    };

    if (hasBody) {
      const payload = useJson && !isFormData(body) ? JSON.stringify(body) : body;
      xhr.send(payload);
    } else {
      xhr.send();
    }
  });
}

function coreRequest(options) {
  const {
    url,
    method = 'GET',
    params,
    data,
    headers,
    baseURL = DEFAULT_BASE_URL,
    timeout = DEFAULT_TIMEOUT,
  } = options || {};

  if (!url) return Promise.reject('url 必填');

  const finalUrl = joinURL(baseURL, url) + buildQueryString(params);
  const payload = {
    method: String(method || 'GET').toUpperCase(),
    headers,
    body: data,
    timeout,
  };

  // 若启用 Mock，则强制走 XHR，便于 mockjs 拦截
  // if (typeof window !== 'undefined' && window.__MOCK_ENABLED__) {
  //   return requestWithXHR(finalUrl, payload);
  // }

  if (typeof fetch === 'function') {
    return requestWithFetch(finalUrl, payload);
  }
  return requestWithXHR(finalUrl, payload);
}

function createShortcut(method) {
  return function shortcut(url, config) {
    const cfg = config || {};
    return coreRequest({ url, method, ...cfg });
  };
}

function request(options) {
  return coreRequest(options);
}

request.get = createShortcut('GET');
request.delete = createShortcut('DELETE');
request.head = createShortcut('HEAD');
request.post = function post(url, data, config) {
  const cfg = config || {};
  return coreRequest({ url, method: 'POST', data, ...cfg });
};
request.put = function put(url, data, config) {
  const cfg = config || {};
  return coreRequest({ url, method: 'PUT', data, ...cfg });
};
request.patch = function patch(url, data, config) {
  const cfg = config || {};
  return coreRequest({ url, method: 'PATCH', data, ...cfg });
};

export default request;
